package com.xwltz.common.phone;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.xwltz.common.phone.algorithm.*;
import com.xwltz.common.phone.base.AlgorithmEnum;
import com.xwltz.common.phone.base.PhoneModel;
import org.springframework.core.io.ClassPathResource;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Optional;

import static java.util.Objects.requireNonNull;

/**
 * @description PhoneHelper
 * 数据库版本:202108
 * Hash：2145792333
 * @information on version 1.0 be created by @xwltz at 2021/11/08 14:47
 */
@SuppressWarnings("unused")
public class PhoneHelper {
	private static final Log logger = LogFactory.get();

	private static final String PHONE_NUMBER_GEO_PHONE_DAT = "/data/phone.dat";
	private static final int dataHash = -2145792333;

	public static Optional<PhoneModel> getInfo(String phoneNumber) {
		return getInfo(phoneNumber, AlgorithmEnum.Binary);
	}

	/**
	 * @param phoneNumber 电话号码, 11位, 或前7位
	 * @return 电话号码归属信息
	 */
	public static Optional<PhoneModel> getInfo(String phoneNumber, AlgorithmEnum algorithm) {
		try {
			byte[] allBytes;
			final InputStream inputStream;
			final ByteArrayOutputStream output = new ByteArrayOutputStream();
			String dbPath = requireNonNull(PhoneHelper.class.getResource(PHONE_NUMBER_GEO_PHONE_DAT)).getPath();
			logger.info("PhoneHelper.local.dbPath: {}", dbPath);
			File file = new File(dbPath);

			if (!file.exists()) {
				ClassPathResource resource = new ClassPathResource(PHONE_NUMBER_GEO_PHONE_DAT);
				inputStream = resource.getInputStream();
			} else {
				inputStream = PhoneHelper.class.getResourceAsStream(PHONE_NUMBER_GEO_PHONE_DAT);
			}

			try (inputStream; output) {
				int n;
				byte[] buffer = new byte[1024 * 4];
				while (-1 != (n = requireNonNull(inputStream, "PhoneHelper: Failed to get inputStream.").read(buffer))) {
					output.write(buffer, 0, n);
				}
				allBytes = output.toByteArray();
			}

			int hashCode = Arrays.hashCode(allBytes);
			logger.debug("loaded datasource, size: {}, hash: {}", allBytes.length, hashCode);
			if (hashCode != dataHash) {
				throw new IllegalStateException("Hash of data not match, expect: " + dataHash + ", actually: " + hashCode);
			}

			PhoneAlgorithm phoneAlgorithm;

			switch (algorithm) {
				case AnotherBinary:
					phoneAlgorithm = new AnotherBinarySearchAlgorithm();
					break;
				case ProspectBinary:
					phoneAlgorithm = new ProspectBinarySearchAlgorithm();
					break;
				case Sequence:
					phoneAlgorithm = new SequenceAlgorithm();
					break;
				default:
					phoneAlgorithm = new BinarySearchAlgorithm();
					break;
			}
			phoneAlgorithm.loadData(allBytes);
			return phoneAlgorithm.getInfo(phoneNumber);
		} catch (Exception e) {
			logger.error("failed to init PhoneHelper", e);
			return Optional.empty();
		}
	}
}
